﻿using LD.Host;
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace LD
{
    public class SocketHelper
    {
        private Socket m_socket;
        public event Action<bool> ConnectionEvent;
        public event Action<byte[]> ReceiveDataEvent;
        private bool m_isConnection;
        private IPEndPoint m_ipEndPoint;
        private Encoding m_encoding;
        private object m_objConnect = new object();

        private bool m_exit = false;
        public bool IsConnection
        {
            get { return m_isConnection; }
            set
            {
                m_isConnection = value;
                ConnectionEvent?.Invoke(value);
                if (value)
                {
                    Console.WriteLine("连接成功");
                }
                else
                    Console.WriteLine("连接失败");
            }
        }
        public SocketHelper(string ip, int port, Encoding encoding)
        {
            m_ipEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
            m_encoding = encoding;
        }
        public SocketHelper(string ip, int port) : this(ip, port, Encoding.UTF8)
        {

        }
        private void SetXintiao()
        {
            //byte[] inValue = new byte[] { 1, 0, 0, 0, 0x20, 0x4e, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探测时间20 秒, 间隔侦测时间2 秒
            byte[] inValue = new byte[] { 1, 0, 0, 0, 0x88, 0x13, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探测时间5 秒, 间隔侦测时间2 秒
            m_socket.IOControl(IOControlCode.KeepAliveValues, inValue, null);
        }
        public bool CreateConnect()
        {
            //退出，不再连接
            if (m_exit)
                return false;

            m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            m_socket.SendTimeout = 2000;
            m_socket.ReceiveTimeout = 2000;
            try
            {
                SetXintiao();
                m_socket.BeginConnect(m_ipEndPoint, new AsyncCallback(ConnectionCallBack), m_socket);
            }
            catch (Exception ex)
            {
                IsConnection = false;
                Console.WriteLine(ex.Message);
                //throw;
            }
            return IsConnection;
        }

        /// <summary>
        /// 重新连接
        /// </summary>
        private void Reconnect()
        {
            //if (!m_exit)
            //    m_socket?.BeginConnect(m_ipEndPoint, new AsyncCallback(ConnectionCallBack), m_socket);

            try
            {
                //m_socket?.Shutdown(SocketShutdown.Both);
                //m_socket.Disconnect(false);
                IsConnection = false;
                m_socket?.Shutdown(SocketShutdown.Both);
                m_socket?.Close();
                m_socket.Dispose();
            }
            catch (Exception)
            {
            }
            finally
            {
                CreateConnect();
            }
        }
        /// <summary>
        /// 连接回调
        /// </summary>
        /// <param name="ar"></param>
        private void ConnectionCallBack(IAsyncResult ar)
        {
            lock (m_objConnect)
            {
                Socket socket = ar.AsyncState as Socket;

                try
                {
                    if (socket != null && socket.Connected)
                    {
                        socket.EndConnect(ar);
                        IsConnection = true;
                        StartKeepAlive();
                    }
                }
                catch (Exception ex)
                {
                    IsConnection = false;
                    Log4.Error($"【SocketHelper.CallBack】" + ex.Message);
                    Reconnect();
                }
            }
        }
        public void Send(string sendMessage)
        {
            //if (!m_socket.Connected)
            //    CreateConnect();
            ReceiveString = string.Empty;
            if (sendMessage == null || sendMessage.Length == 0)
                return;
            if (m_socket.Connected)
            {
                try
                {
                    byte[] bs = m_encoding.GetBytes(sendMessage);
                    int flag = m_socket.Send(bs);
                }
                catch (Exception ex)
                {
                    IsConnection = false;
                    CreateConnect();
                    Log4.Error($"【SocketHelper.Send】" + ex.Message);
                }

            }
        }
        public void Write(byte[] buffer)
        {
            m_socket.Send(buffer);
        }
        /// <summary>
        /// 同步接收
        /// </summary>
        /// <param name="waitReceiveTime"></param>
        /// <returns></returns>
        public string Receive(int waitReceiveTime = 500)
        {
            string result = string.Empty;
            if (IsConnection)
            {
                try
                {
                    Stopwatch stopwatch = new Stopwatch();
                    stopwatch.Start();
                    while (stopwatch.ElapsedMilliseconds < waitReceiveTime && string.IsNullOrEmpty(ReceiveString))
                    {
                        Task.Delay(100).Wait();
                    }
                    result = ReceiveString;

                    stopwatch.Stop();
                }
                catch (Exception)
                {
                    throw;
                }
            }
            return result;
        }
        private byte[] buffer = new byte[1024];
        private void StartKeepAlive()
        {
            m_socket?.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), m_socket);
        }
        private string ReceiveString { get; set; }
        private void ReceiveCallBack(IAsyncResult ar)
        {
            try
            {
                if (ar == null) return;
                if (ar.AsyncState == null) return;
                Socket socket = ar.AsyncState as Socket;
                int readByte = socket.EndReceive(ar);
                if (readByte > 0)
                {
                    byte[] temp = new byte[readByte];
                    Array.ConstrainedCopy(buffer, 0, temp, 0, readByte);
                    ReceiveDataEvent?.Invoke(temp);
                    ReceiveString = m_encoding.GetString(temp);
                }
                else
                {
                    //上次连接状态
                    if (m_socket.Connected)
                    {
                        Reconnect();
                        //退出，不在beginReceive
                        return;
                    }
                }
                m_socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), m_socket);
            }
            catch (Exception ex)
            {
                Log4.Error($"【SocketHelper.ReceiveCallBack】" + ex.Message);
                Reconnect();
            }
        }
        private bool IsSocketConnected()
        {
            bool connectStat = true;
            bool blockingState = m_socket.Blocking;
            try
            {
                byte[] tmp = new byte[1];
                m_socket.Blocking = false;
                m_socket.Send(tmp, 0, 0);
                connectStat = true;
            }
            catch (SocketException e)
            {
                if (e.NativeErrorCode.Equals("10035"))
                {
                    connectStat = true;
                }
                else
                {
                    connectStat = false;
                }
            }
            finally
            {
                m_socket.Blocking = blockingState;
            }
            return connectStat;
        }
        public void Closed()
        {
            m_exit = true;
            if (m_socket.Connected)
                m_socket?.Shutdown(SocketShutdown.Both);
            m_socket?.Close();
            m_socket?.Dispose();
        }
    }
    public enum Status
    {
        成功,
        失败
    }
}
